﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Byond.Standalone.PortAudioStuff;
using Mvvm;
using PortAudioSharp;

namespace Byond.Standalone.ViewModels
{
    using NLog;

    class AudioSettingsViewModel : ViewModelBase
    {
        private readonly PortAudioEngine _audioEngine;
        private readonly SettingsManager _settingsManager;

        private void Ctor()
        {
            SelectedSampleRate = (int)_audioEngine.SampleRate;
            SelectedBufferSize = _audioEngine.BufferSize;

            HostApis = PortAudioExtensions
                .GetHostApiInfos()
                .Select(it => new HostApiViewModel(it))
                .ToList();

            // TODO: Wie machen wir hier die Fehlerbehandlung?
            //       Nochmal genau schauen, was schief gehen kann.

            // There is no explicit value for the host API (MME, ASIO, etc.).
            // So we have to look at the input / output device and infer it.
            if (_audioEngine.OutputDevice.Equals(PortAudioExtensions.NoDevice))
            {
                if (_audioEngine.InputDevice.Equals(PortAudioExtensions.NoDevice))
                {
                    _selectedHostApi = PortAudio.Pa_GetHostApiInfo(PortAudio.Pa_GetDefaultHostApi());
                }
                else
                {
                    _selectedHostApi = _audioEngine.InputDevice.TryGetHostApi()
                        ?? HostApis.First().HostApi;
                }
            }
            else
            {
                _selectedHostApi = _audioEngine.OutputDevice.TryGetHostApi()
                    ?? HostApis.First().HostApi;
            }

            UpdateDevices();
        }

        public IEnumerable<int> SampleRates
        {
            get
            {
                return new[] { 44100, 48000, 88200, 96000, 192000 };
            }
        }

        public int SelectedSampleRate { get; set; }
        public uint SelectedBufferSize { get; set; }

        public IEnumerable<HostApiViewModel> HostApis { get; private set; }

        private PortAudio.PaHostApiInfo _selectedHostApi;
        public HostApiViewModel SelectedHostApi
        {
            get { return HostApis.FirstOrDefault(it => it.HostApi.Equals(_selectedHostApi)); }
            set
            {
                _selectedHostApi = value.HostApi;
                UpdateDevices();
            }
        }

        private void UpdateDevices()
        {
            _inputDevices.Clear();
            _outputDevices.Clear();

            _inputDevices.Add(AudioDeviceViewModel.None);
            _selectedHostApi.GetDevices()
                .Select(it => new AudioDeviceViewModel(it))
                .Where(it => it.IsInputDevice)
                .ForEach(_inputDevices.Add);

            _outputDevices.Add(AudioDeviceViewModel.None);
            _selectedHostApi.GetDevices()
                .Select(it => new AudioDeviceViewModel(it))
                .Where(it => it.IsOutputDevice)
                .ForEach(_outputDevices.Add);

            SelectedInputDevice = _inputDevices.FirstOrDefault(it =>
                it.Device.DeviceNumber() == _audioEngine.InputDevice.DeviceNumber());
            if (SelectedInputDevice == null)
                SelectedInputDevice = _inputDevices.FirstOrDefault();

            SelectedOutputDevice = _outputDevices.FirstOrDefault(it =>
                it.Device.DeviceNumber() == _audioEngine.OutputDevice.DeviceNumber());
            if (SelectedOutputDevice == null)
                SelectedOutputDevice = _outputDevices.FirstOrDefault();
        }

        private readonly ObservableCollection<AudioDeviceViewModel> _inputDevices = new ObservableCollection<AudioDeviceViewModel>();
        public IEnumerable<AudioDeviceViewModel> InputDevices { get { return _inputDevices; } }

        private readonly ObservableCollection<AudioDeviceViewModel> _outputDevices = new ObservableCollection<AudioDeviceViewModel>();
        public IEnumerable<AudioDeviceViewModel> OutputDevices { get { return _outputDevices; } }

        private AudioDeviceViewModel _selectedInputDevice;
        public AudioDeviceViewModel SelectedInputDevice
        {
            get { return _selectedInputDevice; }
            set
            {
                _selectedInputDevice = value;
                OnPropertyChanged("SelectedInputDevice");
            }
        }

        private AudioDeviceViewModel _selectedOutputDevice;
        public AudioDeviceViewModel SelectedOutputDevice
        {
            get { return _selectedOutputDevice; }
            set
            {
                _selectedOutputDevice = value;
                OnPropertyChanged("SelectedOutputDevice");
            }
        }

        public void ApplySettings()
        {
            _audioEngine.ReStart(SelectedSampleRate, SelectedBufferSize, SelectedInputDevice.Device, SelectedOutputDevice.Device);

            _settingsManager.UpdateSettings(
                new AudioSettings
                    {
                        BufferSize = SelectedBufferSize,
                        SampleRate = SelectedSampleRate,
                        InputDeviceName = SelectedInputDevice.Device.name,
                        OutputDeviceName = SelectedOutputDevice.Device.name,
                    });
        }
    }
}